package ua.dogar.vectors;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Iterator;
import java.util.NoSuchElementException;

import ua.dogar.exceptions.IncompatibleVectorSizesException;
import ua.dogar.exceptions.VectorIndexOutOfBoundsException;
import ua.dogar.patterns.Observer;



public class LinkedListVector implements Vector{
	private Node firstNode = null;
	private Node lastNode = null;
	private int size;
	public LinkedListVector(double ... values){
		for(int i=0;i<values.length;i++){
			addElement(values[i], i);
		}
		}
	public void addFirstElement(Double value) {
		Node node = new Node(value);
		if (size == 0) {
			lastNode = node;
		} else {
			firstNode.setPrevNode(node);
		}
		node.setNextNode(firstNode);
		firstNode = node;
		size++;
	}

	public void addLastElement(Double value) {
		Node node = new Node(value);
		if (size == 0)
			firstNode = node;
		else {
			lastNode.setNextNode(node);
			node.setPrevNode(lastNode);
		}
		lastNode = node;
		size++;
	}

	@Override
	public void setElement(double value, int index) {
		if(index<0 || index>getLength()) throw new VectorIndexOutOfBoundsException(index);
		Node tempNode = null;
		//assert (index >= 0 && index < size);
		tempNode = firstNode;
		for (int i = 0; i < index; i++)
			tempNode = tempNode.getNextNode();
		tempNode.setValue(value);

	}

	public void addElement(double value, int index) {
		if(index<0 || index>size) throw new VectorIndexOutOfBoundsException(index);
		Node tempNode = null;
		if (size != 0) {
			tempNode = firstNode;
			if (index == 0)
				tempNode = tempNode.getNextNode();
			for (int i = 0; i < index - 1; i++)
				tempNode = tempNode.getNextNode();
			lastNode = tempNode.getNextNode();
			Node node = new Node(value);
			node.setPrevNode(tempNode);
			node.setNextNode(lastNode);
			tempNode.setNextNode(node);
			if (lastNode != null)
				lastNode.setPrevNode(node);
			else
				node.setNextNode(firstNode);
			size++;
		} else {
			addFirstElement(value);
		}
	}

	@Override
	public int getLength() {
		return size;
	}

	@Override
	public double getElement(int index) {
		if(index<0 || index>size) throw new VectorIndexOutOfBoundsException(index);
		Node tempNode = null;
		tempNode = firstNode;
		for (int i = 0; i < index; i++)
			tempNode = tempNode.getNextNode();
		return tempNode.getValue();

	}

	@Override
	public void setElementsFromArray(double[] arr) throws IncompatibleVectorSizesException{
		if(arr!=null){
		if(arr.length!=getLength()) throw new IncompatibleVectorSizesException();	
		clear();
		for (int i = 0; i < arr.length; i++) {
			addElement(arr[i], i);
			}
		}
	}

	@Override
	public void setElementsFromVector(Vector v) throws IncompatibleVectorSizesException{
		if(v!=null){
			if(v.getLength()!=getLength()) throw new IncompatibleVectorSizesException();
		clear();
		for (int i = 0; i < v.getLength(); i++)
			addElement(v.getElement(i), i);
		}
	}

	@Override
	public Vector mulVector(int num) {
		for (int i = 0; i < size; i++)
			setElement(getElement(i) * num, i);
		return this;
	}

	@Override
	public void addVector(Vector v) throws IncompatibleVectorSizesException{
		if(v!=null){
			if(v.getLength()!=getLength()) throw new IncompatibleVectorSizesException();
			for (int i = 0; i < v.getLength(); i++)
				setElement(getElement(i) + v.getElement(i), i);
		}
	}

	@Override
	public boolean compare(Vector v) {
		if(v!=null){
		if (v.getLength() == size) {
			for (int i = 0; i < v.getLength(); i++)
				if (getElement(i) != v.getElement(i))
					return false;
			return true;
		} else
			return false;
		} else return false;
	}
	public double removeElement(int index) {
		if(index<0 || index>size) throw new VectorIndexOutOfBoundsException(index);
		Node tempNode = null;
		tempNode = firstNode;
		if (index == 0) {
			Double value = firstNode.getValue();
			firstNode = firstNode.getNextNode();
			size--;
			return value;
		} else if (index == size) {
			Double value = lastNode.getValue();
			lastNode = lastNode.getPrevNode();
			size--;
			return value;
		}
		for (int i = 0; i < index - 1; i++)
			tempNode = tempNode.getNextNode();
		Node two = tempNode.getNextNode();
		tempNode.setNextNode(two.getNextNode());
		Double value = two.getValue();
		two = null;
		size--;
		return value;
	}

	public void clear() {
		firstNode = null;
		size = 0;
	}

	@Override
	public String toString() {
		Node node = firstNode;
		if(node!=null){
		StringBuffer stringOut = new StringBuffer();
		stringOut.append("LinkedListVector [vector=");
		do {
			stringOut.append((node.getValue()) + " ");
			node = node.getNextNode();
		} while (node != firstNode);
		stringOut.append("]");
		return stringOut.toString();}
		else return null;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (obj instanceof Vector) {
			Vector temp = (Vector) obj;
			if (temp.getLength() == size) {
				for (int i = 0; i < size; i++)
					if (temp.getElement(i) != this.getElement(i))
						return false;
				return true;
			}
		}
		return false;
	}

	@Override
	public int hashCode() {
		int result = 0;
		for (Double d : this) {
			long bits = Double.doubleToRawLongBits(d);
			result ^= ((int) (bits & 0x00000000FFFFFFFFL))
					^ ((int) ((bits & 0xFFFFFFFF00000000L)));
		}
		return result;
	}

	@Override
	protected Object clone() throws CloneNotSupportedException {
		LinkedListVector clone = (LinkedListVector) super.clone();
		clone.clear();
		for (int i = 0; i < size; i++)
			clone.addLastElement(this.getElement(i));
		return clone;
	}

	class MyIterator implements Iterator<Double> {
		int counter = 0;
		@Override
		public boolean hasNext() {
			return counter!=size;
		}

		@Override
		public Double next() {
			if(!hasNext()) throw new NoSuchElementException();
			return getElement(counter++);
			
		}
		@Override
		public void remove() {

		}

	}

	@Override
	public Iterator<Double> iterator() {
		return new MyIterator();
	}
	
	class Node implements Cloneable,Serializable{
		private Node prevNode;
		private Node nextNode;
		private double value;
		public Node(Double value) {
		    this.value = value;
		  }

		public Node getPrevNode() {
			return prevNode;
		}
		public void setPrevNode(Node prevNode) {
			this.prevNode = prevNode;
		}
		public Node getNextNode() {
			return nextNode;
		}
		public void setNextNode(Node nextNode) {
			this.nextNode = nextNode;
		}
		public Double getValue() {
			return value;
		}
		public void setValue(Double value) {
			this.value = value;
		}
		@Override
		protected Object clone() throws CloneNotSupportedException {
			Node clone = (Node) super.clone();
			return clone;
		}
	}

	@Override
	public void registerObserver(Observer o) {
		
	}
	@Override
	public void removeObserver(Observer o) {
		
	}
	@Override
	public void notifyObservers(int index) {
		
	}
	@Override
	public void notifyObservers() {

		
	}
}
